#pragma once
#include <string.h>
#include <string>
#include <list>

#include "linenoise.h"
#include <vector>
using namespace std;
#define BACKUPFS_MAX_CMD_LENGTH 4096
#define CHECK_BOOL_TRUE std::string("CHECK_TRUE ")
#define CHECK_BOOL_FALSE std::string("CHECK_FALSE ")

class BackupFSShell
{
public:
	BackupFSShell();
	virtual ~BackupFSShell();
	virtual bool RunCmdLines(const char* exe_name,int argc, wchar_t* argv[]);
	void RunBatchCmds(const std::wstring& file_name);
	// linenoise callback functions
	static void Completion(const char *buf, linenoiseCompletions *lc);
	static std::string Hints(const char *buf);

private:
	static bool RunCmd(const char* cmd);

};

class BackupFSShellCmd
{
public:
	virtual bool RunCmdFunction(const std::vector<std::string>&args) = 0;
	virtual ~BackupFSShellCmd(){}
	virtual const char* GetCmdName() = 0;
	virtual const char* GetCmdHelpText() = 0;

};

class BackupFSShellCmdBase {
public:
	virtual BackupFSShellCmd* CreateCmd() = 0;
	virtual ~BackupFSShellCmdBase() {}
};

template <class T>
class BackupFSShellCmdImpl : public BackupFSShellCmdBase
{
public:
	virtual BackupFSShellCmd* CreateCmd() { return new T; }
};
class BackupFSShellCmdase_c
{
public:
	static BackupFSShellCmdase_c* GetInstance()
	{
		static BackupFSShellCmdase_c instance;
		return &instance;
	}
	static BackupFSShellCmd *AddCmd(BackupFSShellCmdBase *c)
	{
		BackupFSShellCmd * cmd = c->CreateCmd();
		GetInstance()->cmds.push_back(cmd);
		delete c;
		return cmd;
	}
	
	static void GetAllCommands(std::vector<std::string>&commands)
	{
		commands.push_back("list");
		commands.push_back("quit");
		for (auto cmd: BackupFSShellCmdase_c::GetInstance()->cmds)
		{
			string cmd_name = cmd->GetCmdName() ;
			commands.push_back(cmd_name);
		}

	}
	static void ListAllCommands()
	{
		printf("%s %s", "list ", "list all the commands\n");
		printf("%s %s", "quit ", "exit the program\n");
		for (auto cmd: BackupFSShellCmdase_c::GetInstance()->cmds)
		{
			string cmd_name = cmd->GetCmdName() ;
			printf("%s %s\n", cmd_name.c_str(), cmd->GetCmdHelpText());
		}
	}

	static bool RunCommand(const char* command,bool & is_inner_cmd)
	{
		std::vector<std::string>args;
		std::string command_name;
		std::string cmd_str = command;
		if (cmd_str.length() < 1)
			return false;
		//support " " and "\""
		//for example:dotest arg1 arg2 "arg arg"
		int first_char = -1;
		int first_qm = -1;
		for (int i = 0; i < (int)cmd_str.length(); i++)
		{
			char c = cmd_str.at(i);
			switch (c)
			{
			case ' ':
				if (first_char > -1 && first_qm == -1)
				{
					if(command_name.length() == 0)
						command_name = cmd_str.substr(first_char, i - first_char);
					else
					{
						args.push_back(cmd_str.substr(first_char, i - first_char));
					}
					first_char =-1;
				}
				break;
			case '\"':
				if (first_qm != -1)
				{
					//argu
					args.push_back(cmd_str.substr(first_qm+1, i - first_qm-1));
					first_qm = -1;
					first_char = -1;
				}
				else
				{
					first_qm = i;
					first_char = -1;
				}
				break;
			default:
				if(first_char == -1 && first_qm == -1)
					first_char = i;
				break;
			}
		}
		if (first_qm != -1)
		{
			args.push_back(cmd_str.substr(first_qm + 1, cmd_str.length() - first_qm - 1));
		}
		else if (first_char != -1)
		{
			if (command_name.length() == 0)
			{
				command_name = cmd_str.substr(first_char, cmd_str.length() + 1 - first_char);
			}
			else
			{
				args.push_back(cmd_str.substr(first_char, cmd_str.length() + 1 - first_char));
			}
		}
		for (auto cmd: BackupFSShellCmdase_c::GetInstance()->cmds)
		{
			string cmd_name = cmd->GetCmdName();
			if (strcmp(cmd_name.c_str(), command_name.c_str()) == 0)
			{
				is_inner_cmd = true;
				return cmd->RunCmdFunction(args);
			}
		}	
		is_inner_cmd = false;
		return false;
	}

	std::vector<BackupFSShellCmd*> cmds;
};

#define BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name,cmd_help_text) \
	cmd_name##_##_BackupFSShellCmd
#define BACKUPFS_DO_CMD_(cmd_name,cmd_help_text, parent_class)\
class BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name,cmd_help_text) : public parent_class {\
public:\
	BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name, cmd_help_text)() \
	{\
		_command_name = #cmd_name; \
		_help_string = #cmd_help_text;\
	}\
public:\
	virtual bool RunCmdFunction(const std::vector<std::string>&args);\
	const char* GetCmdName()\
	{\
		return _command_name.c_str();\
	}\
	const char* GetCmdHelpText()\
	{\
		return _help_string.c_str();\
	}\
private:\
	static  BackupFSShellCmd * const _new_object;\
	std::string _command_name;\
	std::string _help_string;\
};\
	BackupFSShellCmd* const BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name,cmd_help_text)::_new_object = BackupFSShellCmdase_c::AddCmd(\
	new BackupFSShellCmdImpl<\
	BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name, cmd_help_text)>);\
	bool BACKUPFS_DO_CMD_CLASS_NAME_(cmd_name, cmd_help_text)::RunCmdFunction(const std::vector<std::string>&args)


#define BACKUPFS_CMD(cmd_name, cmd_help_text)\
	BACKUPFS_DO_CMD_(cmd_name, cmd_help_text,BackupFSShellCmd)
